import {Tab, Tabs} from 'nextra-theme-docs';
import Callout from 'components/Callout';
import Steps from 'components/Steps';
import Details from 'components/Details';

# App Router setup with i18n routing

In order to use unique pathnames for every language that your app supports, `next-intl` can be used to handle the following routing setups:

1. Prefix-based routing (e.g. `/en/about`)
2. Domain-based routing (e.g. `en.example.com/about`)

In either case, `next-intl` integrates with the App Router by using a top-level `[locale]` [dynamic segment](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes) that can be used to provide content in different languages.

## Getting started

If you haven't done so already, [create a Next.js app](https://nextjs.org/docs/getting-started/installation) that uses the App Router and run:

```sh
npm install next-intl
```

Now, we're going to create the following file structure:

```
├── messages (1)
│   ├── en.json
│   └── ...
├── next.config.mjs (2)
└── src
    ├── i18n.ts (3)
    ├── middleware.ts (4)
    └── app
        └── [locale]
            ├── layout.tsx (5)
            └── page.tsx (6)
```

**Let's set up the files:**

<Steps>

### `messages/en.json` [#messages]

Messages can be provided locally or loaded from a remote data source (e.g. a translation management system). Use whatever suits your workflow best.

The simplest option is to add JSON files in your project based on locales—e.g. `en.json`.

```json filename="messages/en.json"
{
  "HomePage": {
    "title": "Hello world!"
  }
}
```

### `next.config.mjs` [#next-config]

Now, set up the plugin which creates an alias to provide your i18n configuration (specified in the next step) to Server Components.

<Tabs items={['next.config.mjs', 'next.config.js']}>
<Tab>

```js filename="next.config.mjs"
import createNextIntlPlugin from 'next-intl/plugin';

const withNextIntl = createNextIntlPlugin();

/** @type {import('next').NextConfig} */
const nextConfig = {};

export default withNextIntl(nextConfig);
```

</Tab>
<Tab>

```js filename="next.config.js"
const createNextIntlPlugin = require('next-intl/plugin');

const withNextIntl = createNextIntlPlugin();

/** @type {import('next').NextConfig} */
const nextConfig = {};

module.exports = withNextIntl(nextConfig);
```

</Tab>
</Tabs>

### `i18n.ts` [#i18nts]

`next-intl` creates a request-scoped configuration object that can be used to provide messages and other options based on the user's locale for usage in Server Components.

```tsx filename="src/i18n.ts"
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';

// Can be imported from a shared config
const locales = ['en', 'de'];

export default getRequestConfig(async ({locale}) => {
  // Validate that the incoming `locale` parameter is valid
  if (!locales.includes(locale as any)) notFound();

  return {
    messages: (await import(`../messages/${locale}.json`)).default
  };
});
```

<Details id="move-i18n-ts">
<summary>Can I move this file somewhere else?</summary>

This file is supported out-of-the-box both in the `src` folder as well as in the project root with the extensions `.ts`, `.tsx`, `.js` and `.jsx`.

If you prefer to move this file somewhere else, you can optionally provide a path to the plugin:

```js filename="next.config.mjs"
const withNextIntl = createNextIntlPlugin(
  // Specify a custom path here
  './somewhere/else/i18n.ts'
);
```

</Details>

### `middleware.ts` [#middleware]

The middleware matches a locale for the request and handles redirects and rewrites accordingly.

```tsx filename="src/middleware.ts"
import createMiddleware from 'next-intl/middleware';

export default createMiddleware({
  // A list of all locales that are supported
  locales: ['en', 'de'],

  // Used when no locale matches
  defaultLocale: 'en'
});

export const config = {
  // Match only internationalized pathnames
  matcher: ['/', '/(de|en)/:path*']
};
```

### `app/[locale]/layout.tsx` [#layout]

The `locale` that was matched by the middleware is available via the `locale` param and can be used to configure the document language. Additionally, we can use this place to pass configuration from `i18n.ts` to Client Components via `NextIntlClientProvider`.

```tsx filename="app/[locale]/layout.tsx"
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';

export default async function LocaleLayout({
  children,
  params: {locale}
}: {
  children: React.ReactNode;
  params: {locale: string};
}) {
  // Providing all messages to the client
  // side is the easiest way to get started
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
```

Note that `NextIntlClientProvider` automatically inherits configuration from `i18n.ts` here.

### `app/[locale]/page.tsx` [#page]

Use translations in your page components or anywhere else!

```tsx filename="app/[locale]/page.tsx"
import {useTranslations} from 'next-intl';

export default function HomePage() {
  const t = useTranslations('HomePage');
  return <h1>{t('title')}</h1>;
}
```

</Steps>

That's all it takes!

In case you ran into an issue, have a look at [the App Router example](/examples#app-router) to explore a working app.

<Callout>

**Next steps:**

<ul className="ml-4 list-disc">
  <li>[Usage guide](/docs/usage): Format messages, dates and times</li>
  <li>
    [Routing](/docs/routing): Integrate i18n routing with `<Link />` & friends
  </li>
  <li>
    [Workflows](/docs/workflows): Make use of the TypeScript integration & more
  </li>
</ul>

</Callout>

## Static rendering

When using the setup with i18n routing, `next-intl`will currently opt into dynamic rendering when APIs like `useTranslations` are used in Server Components. This is a limitation that we aim to remove in the future, but as a stopgap solution, `next-intl` provides a temporary API that can be used to enable static rendering:

<Steps>

### Add `generateStaticParams` to `app/[locale]/layout.tsx`

Since we are using a dynamic route segment for the `[locale]` param, we need to pass all possible values to Next.js via [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) so that the routes can be rendered at build time.

```tsx filename="app/[locale]/layout.tsx"
// Can be imported from a shared config
const locales = ['en', 'de'];

export function generateStaticParams() {
  return locales.map((locale) => ({locale}));
}
```

### Add `unstable_setRequestLocale` to all layouts and pages

`next-intl` provides a temporary API that can be used to distribute the locale that is received via `params` in layouts and pages for usage in all Server Components that are rendered as part of the request.

```tsx filename="app/[locale]/layout.tsx"
import {unstable_setRequestLocale} from 'next-intl/server';

export default async function LocaleLayout({children, params: {locale}}) {
  unstable_setRequestLocale(locale);

  return (
    // ...
  );
}
```

```tsx filename="app/[locale]/page.tsx"
import {unstable_setRequestLocale} from 'next-intl/server';

export default function IndexPage({params: {locale}}) {
  unstable_setRequestLocale(locale);

  // Once the request locale is set, you
  // can call hooks from `next-intl`
  const t = useTranslations('IndexPage');

  return (
    // ...
  );
}
```

**Keep in mind that:**

1. The locale that you pass to `unstable_setRequestLocale` should be validated (e.g. in [`i18n.ts`](/docs/usage/configuration#i18nts)).

2. You need to call this function in every page and every layout that you intend to enable static rendering for since Next.js can render layouts and pages independently.

E.g. when you navigate from `/settings/profile` to `/settings/privacy`, the `/settings` segment might not re-render as part of the request. Due to this, it's important that `unstable_setRequestLocale` is called not only in the parent `settings/layout.tsx`, but also in the individual pages `profile/page.tsx` and `privacy/page.tsx`.

<Details id="setrequestlocale-unstable">
<summary>What does "unstable" mean?</summary>

`unstable_setRequestLocale` is meant to be used as a stopgap solution and we aim to remove it in the future [in case Next.js adds an API to access parts of the URL](https://github.com/vercel/next.js/discussions/58862). If that's the case, you'll get a deprecation notice in a minor version and the API will be removed as part of a major version.

That being said, the API is expected to work reliably if you're cautious to apply it in all relevant places.

</Details>

<Details id="setrequestlocale-implementation">
<summary>How does unstable_setRequestLocale work?</summary>

`next-intl` uses [`cache()`](https://react.dev/reference/react/cache) to create a mutable store that holds the current locale. By calling `unstable_setRequestLocale`, the current locale will be written to the store, making it available to all APIs that require the locale.

Note that the store is scoped to a request and therefore doesn't affect other requests that might be handled in parallel while a given request resolves asynchronously.

</Details>

<Details id="setrequestlocale-background">
<summary>Why is this API necessary?</summary>

Next.js currently doesn't provide an API to read route params like `locale` at arbitrary places in Server Components (see [`vercel/next.js`#58862](https://github.com/vercel/next.js/discussions/58862)). The `locale` is fundamental to all APIs provided by `next-intl`, therefore passing this as a prop throughout the tree doesn't stand out as particularly ergonomic.

Due to this, `next-intl` uses its middleware to attach an `x-next-intl-locale` header to the incoming request, holding the negotiated locale as a value. This technique allows the locale to be read at arbitrary places via `headers().get('x-next-intl-locale')`.

However, the usage of `headers` opts the route into dynamic rendering.

By using `unstable_setRequestLocale`, you can provide the locale that is received in layouts and pages via `params` to `next-intl`. All APIs from `next-intl` can now read from this value instead of the header, enabling static rendering.

</Details>

### Use the `locale` param in metadata

In addition to the rendering of your pages, also page metadata needs to qualify for static rendering.

To achieve this, you can forward the `locale` that you receive from Next.js via `params` to [the awaitable functions from `next-intl`](/docs/environments/server-client-components#async-components).

```tsx filename="page.tsx"
import {getTranslations} from 'next-intl/server';

export async function generateMetadata({params: {locale}}) {
  const t = await getTranslations({locale, namespace: 'Metadata'});

  return {
    title: t('title')
  };
}
```

</Steps>
